/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef HIVIEWDFX_HICOLLIE_H
#define HIVIEWDFX_HICOLLIE_H
/**
 * @addtogroup HiCollie
 * @{
 *
 * @brief 提供检测业务线程卡死或卡顿的能力。请注意：要在非业务线程中调用。
 *
 * 本模块函数可用于：\n
 *（1）注册应用业务线程卡死的周期性检测任务；\n
 *（2）注册应用业务线程卡顿检测的回调函数；\n
 *（3）上报应用业务线程卡死事件。
 *
 * @since 12
 */

/**
 * @file hicollie.h
 *
 * @brief HiCollie模块对外提供检测业务线程卡死、卡顿，以及上报卡死事件的能力。
 * @kit PerformanceAnalysisKit
 * @include <hicollie/hicollie.h>
 * @library libohhicollie.so
 * @syscap SystemCapability.HiviewDFX.HiCollie
 * @since 12
 */

#include <time.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/**
 * @brief 错误码定义。
 *
 * @since 12
 */
typedef enum HiCollie_ErrorCode  {
    /** 成功。 */
    HICOLLIE_SUCCESS  = 0,
    /** 无效参数。*/
    HICOLLIE_INVALID_ARGUMENT  = 401,
    /** 调用线程错误。*/
    HICOLLIE_WRONG_THREAD_CONTEXT = 29800001,
    /** 远程调用错误。 */
    HICOLLIE_REMOTE_FAILED = 29800002,
    /** 无效的函数执行超时检测器名称。
     * @since 18
     */
    HICOLLIE_INVALID_TIMER_NAME = 29800003,
    /** 无效的函数执行超时时间阈值。
     * @since 18
     */
    HICOLLIE_INVALID_TIMEOUT_VALUE = 29800004,
    /** 函数执行超时检测接入进程错误。
     * @since 18
     */
    HICOLLIE_WRONG_PROCESS_CONTEXT = 29800005,
    /** 用于保存返回的计时器id的指针不应为NULL。
     * @since 18
     */
    HICOLLIE_WRONG_TIMER_ID_OUTPUT_PARAM = 29800006,
} HiCollie_ErrorCode;

/**
 * @brief 在业务线程卡死检测中，通过实现该函数来检测业务线程是否卡住。\n
 * HiCollie将在独立线程中每3秒调用一次该函数。\n
 * 例如：该函数可实现向业务线程发送消息，在业务线程接收到消息之后，设置一个标记，检查这个标记，确定业务线程是否卡住。
 *
 * @since 12
 */
typedef void (*OH_HiCollie_Task)(void);

/**
 * @brief 卡顿检测中,函数用于记录业务线程处理事件的开始时间。\n
 * 检查处理事件的执行时间，HiCollie将检查每个事件的持续时间。如果超过预设阈值，上报jank事件。\n
 * 该函数是在每个事件处理之前插入的回调函数。
 * 
 * @param eventName 业务线程处理事件的名字。
 * @since 12
 */
typedef void (*OH_HiCollie_BeginFunc)(const char* eventName);

/**
 * @brief 卡顿检测中, 该函数用于检测业务线程处理事件是否卡顿。\n
 * 检查处理事件的执行时间，HiCollie将检查每个事件的持续时间。如果超过预设阈值，上报jank事件。\n
 * 该函数是在每个事件处理之后插入的回调函数。
 *
 * @param eventName 业务线程处理事件的名字。
 * @since 12
 */
typedef void (*OH_HiCollie_EndFunc)(const char* eventName);

/**
 * @brief 检测业务线程卡顿的相关参数。请注意，API 12及以上支持。
 *
 * @since 12
 */
typedef struct HiCollie_DetectionParam {
    /** 扩展参数以供将来使用。 */
    int sampleStackTriggerTime;
    /** 扩展参数以供将来使用。 */
    int reserved;
} HiCollie_DetectionParam;

/**
 * @brief 注册应用业务线程卡死的周期性检测任务。用户实现回调函数, 用于定时检测业务线程卡死情况。\n
 * 默认检测时间：3s上报BUSSINESS_THREAD_BLOCK_3S告警事件，6s上报BUSSINESS_THREAD_BLOCK_6S卡死事件。
 *
 * @param task 每3秒执行一次的周期性检测任务，用于检测业务线程是否卡住。
 * @return {@link HICOLLIE_SUCCESS} 0 - 成功。\n
 *         {@link HICOLLIE_WRONG_THREAD_CONTEXT} 29800001 - 调用线程错误。在非主线程中调用该函数。\n
 *         具体可参考{@link HiCollie_ErrorCode}。
 * @since 12
 */
HiCollie_ErrorCode OH_HiCollie_Init_StuckDetection(OH_HiCollie_Task task);

/**
 * @brief 注册应用业务线程卡死的周期性检测任务。用户实现回调函数, 用于定时检测业务线程卡死情况。\n
 * 开发者可以设置卡死检测时间，可设置的时间范围：[3, 15]，单位：秒。
 *
 * @param task 每stuckTimeout时间执行一次的周期性检测任务，用于检测业务线程是否卡住。
 * @param stuckTimeout 检测业务线程卡死时间。任务执行超过stuckTimeout时间上报卡死告警事件；任务超过stuckTimeout * 2时间上报卡死事件。\n
 *         单位：s。规定：最大值15s，最小值3s。
 * @return {@link HICOLLIE_SUCCESS} 0 - 成功。\n
 * 		   {@link HICOLLIE_INVALID_ARGUMENT} 401 - 卡死检测时间设置错误。\n
 *         {@link HICOLLIE_WRONG_THREAD_CONTEXT} 29800001 - 调用线程错误。在非主线程中调用该函数。\n
 *         具体可参考{@link HiCollie_ErrorCode}。
 * @since 18
 */
HiCollie_ErrorCode OH_HiCollie_Init_StuckDetectionWithTimeout(OH_HiCollie_Task task, uint32_t stuckTimeout);

/**
 * @brief 注册应用业务线程卡顿检测的回调函数。\n
 * 线程卡顿监控功能需要开发者实现两个卡顿检测回调函数, 分别放在业务线程处理事件的前后。作为插桩函数，监控业务线程处理事件执行情况。
 *
 * @param beginFunc 检测业务线程处理事件前的函数。
 * @param endFunc 检测业务线程处理事件后的函数。
 * @param param 扩展参数以供将来使用。
 * @return {@link HICOLLIE_SUCCESS} 0 - 成功。\n
 * 		   {@link HICOLLIE_INVALID_ARGUMENT} 401 - 开始函数和结束函数两者都必须有值或为空，否则将返回该错误值。\n
 *         {@link HICOLLIE_WRONG_THREAD_CONTEXT} 29800001 - 调用线程错误。在非主线程中调用该函数。\n
 *         具体可参考{@link HiCollie_ErrorCode}。
 * @since 12
 */
HiCollie_ErrorCode OH_HiCollie_Init_JankDetection(OH_HiCollie_BeginFunc* beginFunc,
    OH_HiCollie_EndFunc* endFunc, HiCollie_DetectionParam param);

/**
 * @brief 上报应用业务线程卡死事件，生成卡死故障日志，辅助定位应用卡死问题。\n
 * 先调用OH_HiCollie_Init_StuckDetection或OH_HiCollie_Init_StuckDetectionWithTimeout接口，初始化检测的task；\n
 * 如果task任务超时，结合业务逻辑，调用OH_HiCollie_Report接口上报卡死事件。
 *
 * @param isSixSecond 布尔指针。布尔指针的值。如果卡住6秒，则为真。如果卡住3秒，则为False。
 * @return {@link HICOLLIE_SUCCESS} 0 - 成功。\n
 * 		   {@link HICOLLIE_INVALID_ARGUMENT} 401 - 开始函数和结束函数两者都必须有值或为空，否则将返回该错误值。\n
 *         {@link HICOLLIE_WRONG_THREAD_CONTEXT} 29800001 - 调用线程错误。在非主线程中调用该函数。\n
 *         {@link HICOLLIE_REMOTE_FAILED} 29800002 - 远程调用错误。请求IPC远程服务失败。\n
 *         具体可参考{@link HiCollie_ErrorCode}。
 * @since 12
 */
HiCollie_ErrorCode OH_HiCollie_Report(bool* isSixSecond);

/**
 * @brief 当用户调用{@link OH_HiCollie_SetTimer}后，未在其自定义的任务超时时间阈值内调用{@link OH_HiCollie_CancelTimer}，回调函数将被执行。
 *
 * @since 18
 */
typedef void (*OH_HiCollie_Callback)(void*);

/**
 * @brief 定义函数执行超时时发生的动作。
 *
 * @since 18
 */
typedef enum HiCollie_Flag {
    /** 默认动作，生成日志及执行恢复动作。 */
    HICOLLIE_FLAG_DEFAULT = (~0),
    /* 仅执行回调函数。 */
    HICOLLIE_FLAG_NOOP = (0),
    /* 生成日志。 */
    HICOLLIE_FLAG_LOG = (1 << 0),
    /* 执行恢复动作。 */
    HICOLLIE_FLAG_RECOVERY = (1 << 1)
} HiCollie_Flag;

/**
* @brief 定义OH_HiCollie_SetTimer函数的输入参数。
*
* @since 18
*/
typedef struct HiCollie_SetTimerParam {
    /** timer任务名称。 */
    const char *name;
    /** 任务超时时间阈值，单位s。 */
    unsigned int timeout;
    /** 超时发生时执行的回调函数。 */
    OH_HiCollie_Callback func;
    /** 回调函数的参数。 */
    void *arg;
    /** 超时发生时执行的动作，参考{@link HiCollie_Flag}。 */
    HiCollie_Flag flag;
} HiCollie_SetTimerParam;

/**
 * @brief 注册定时器，用于检测函数或代码块执行是否超过自定义时间。\n
 * 结合OH_HiCollie_CancelTimer接口配套使用，应在调用耗时的函数之前使用。
 *
 * @param param 定义输入参数。
 * @param id 用于保存返回的计时器id的指针，它不应为NULL。
 * @return {@link HICOLLIE_SUCCESS} 0 - 成功。\n
 * 		   {@link HICOLLIE_INVALID_TIMER_NAME} 29800003 - 无效的计时器名称，不应为NULL或空字符串。\n
 *         {@link HICOLLIE_INVALID_TIMEOUT_VALUE} 29800004 - 无效的超时值。\n
 *         {@link HICOLLIE_WRONG_PROCESS_CONTEXT} 29800005 - 无效的接入检测进程上下文，appspawn与nativespawn进程中不可调用。\n
 *         {@link HICOLLIE_WRONG_TIMER_ID_OUTPUT_PARAM} 29800006 - 用于保存返回的计时器id的指针，不应该为NULL。\n
 *         具体可参考{@link HiCollie_ErrorCode}。
 * @since 18
 */
HiCollie_ErrorCode OH_HiCollie_SetTimer(HiCollie_SetTimerParam param, int *id);

/**
 * @brief  取消定时器。\n
 * 结合OH_HiCollie_SetTimer接口配套使用，执行函数或代码块后使用，OH_HiCollie_CancelTimer通过id将该任务取消；\n
 * 若未在自定义时间内取消，则执行回调函数，在特定自定义超时动作下，生成故障日志。
 *
 * @param id 执行{@link OH_HiCollie_SetTimer}函数后更新的计时器id。
 * @since 18
 */
void OH_HiCollie_CancelTimer(int id);
#ifdef __cplusplus
}
#endif
/** @} */

#endif // HIVIEWDFX_HICOLLIE_H
